﻿using System;
using System.Collections.Generic;
using System.Linq;
using IcicleFramework.Components;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;

namespace IcicleFramework.Entities
{
    public class GameObject : IGameObject
    {
        #region Internal Members

        private Vector2 position;

        private bool destroyed;

        private bool active;
        
        #endregion


        #region Properties

        public Guid GUID { get; private set; }

        public bool Active
        {
            get { return active; }

            set
            {
                if (active != value && components != null)
                {
                    foreach (IBaseComponent component in components.Values)
                        component.Active = value;

                    active = value;
                }
            }
        }
        
        public bool Destroyed
        {
            get { return destroyed; }
            set
            {
                destroyed = true;

                if (value && OnDestroyed != null)
                    OnDestroyed(this);

                Dispose();
            }
        }

        public bool HasMoved { get; set; }

        public Vector2 Position
        {
            get { return position; }
            set
            {
                if (!value.Equals(position))
                {
                    position = value;
                    HasMoved = true;
                }
            }
        }

        private Dictionary<Type, IBaseComponent> components; 

        #endregion


        #region Events

        public event MoveHandler OnMove;

        public event DestroyedHandler OnDestroyed;

        #endregion


        #region Constructors

        /// <summary>
        /// Creates a new GameObject with basic initialized settings.
        /// </summary>
        public GameObject()
        {
            HasMoved = false;
            OnMove = null;

            components = new Dictionary<Type, IBaseComponent>();

            GUID = Guid.NewGuid();
        }
        
        /// <summary>
        /// Deep-Copy Constructor for a GameObject.
        /// </summary>
        /// <param name="old">The GameObject to create a deep-copy of.</param>
        private GameObject(GameObject old)
        {
            HasMoved = old.HasMoved;
            Active = old.Active;

            OnMove = null;
            OnDestroyed = null;

            components = new Dictionary<Type, IBaseComponent>();

            foreach (KeyValuePair<Type, IBaseComponent> compEntry in old.components)
                components.Add(compEntry.Key, compEntry.Value.DeepClone());

            GUID = Guid.NewGuid();
        }

        #endregion


        #region Component Manipulation

        public T GetComponent<T>() where T : IBaseComponent
        {
            T foundComponent = default(T);

            if (components.ContainsKey(typeof(T)))
            {
                foundComponent = (T) components[typeof (T)];
            }

            return foundComponent;
        }

        public void AddComponent(Type baseInterfaceType, IBaseComponent component)
        {
            if (!components.ContainsKey(baseInterfaceType))
            {
                components.Add(baseInterfaceType, component);
            }
        }

        public List<IBaseComponent> AllComponents()
        {
            return components.Values.ToList();
        }

        #endregion


        #region Update, Load, Initialize

        public void Initialize()
        {
            foreach (IBaseComponent component in components.Values)
                component.Initialize();
        }

        public void Update(GameTime gameTime)
        {
            if (HasMoved)
            {
                if (OnMove != null)
                    OnMove(this);

                HasMoved = false;
            }

            foreach (IBaseComponent component in components.Values)
            {
                component.Update(gameTime);
            }
        }
        
        public void Load(ContentManager content)
        {
            foreach (IBaseComponent component in components.Values)
            {
                ILoadable loadable = component as ILoadable;

                if (loadable != null)
                    loadable.Load(content);
            }
        }

        #endregion


        #region Cloning

        public IGameObject DeepClone()
        {
            return new GameObject(this);
        }

        public void CloneAttributes(IGameObject old)
        {
            GameObject old2 = null;
            this.Active = old.Active;

            if (old is GameObject)
                old2 = old as GameObject;

            if (old2 != null)
            {
                foreach (Type key in old2.components.Keys)
                {
                    this.components.Add(key, old2.components[key].DeepClone());
                }
            }

            this.OnMove = null;
            this.OnDestroyed = null;
        }

        #endregion


        public void Dispose()
        {
            OnMove = null;
            OnDestroyed = null;

            foreach (IBaseComponent component in components.Values)
                component.Dispose();
        }
    }
}
